package com.livestocksolutions;

import java.util.Date;
import java.util.HashMap;

import com.livestocksolutions.util.UpdateHistory;

/* -- Version History --
 *  1.0 : 15 July 2012 - Initial version.
 *  
 *  1.1 : 15 July 2012 - Changed prescribedMedicines from type MedicineHistory[] to ArrayList<MedicineHistory>
 *  
 *  1.2 : 16 July 2012 - Class now implements Serializable
 *  
 *  1.3 : 05 September 2012 - Changed private Business vet to private long vet, added (Business)
 *                   LivestockSolutions.getOwnerByID(vet) to getVet() to return the Business.
 *                   changed setVet() method to store only the id of the Business
 *  
 *  1.4 : 24 September 2012 - Added Update methods that will allow the comparing and updation of a similar Object.
 *                   Serialisation method no longer requires MedicalHistory implementing Serializable.
 */

/**
 * Creates an Object that keeps all the details relating to a single vet visit within
 * a single Object, such as the date, what medicines were prescribed (if any) and the
 * cost of this visit excluding any cost for the medicines prescribed.
 * @author Adam Parr
 * 
 * @version 1.4
 * 
 * @.modified 17 March 2013
 */
public class MedicalHistory{
	/**
	 * The ID of this MedicalItemHistory.
	 */
	protected long id;
	/**
	 * Keep track of any changes of this MedicalItemHistory.
	 */
	protected HashMap<String,UpdateHistory> update = new HashMap<String,UpdateHistory>();
	/**
	 * The Business that did this medical check for the animal
	 */
	private long vet;
	/**
	 * The Date the check occurred
	 */
	private long date;
	/**
	 * The Medications prescribed
	 */
	private HashMap<Long,MedicineHistory> prescribedMedicines = new HashMap<Long,MedicineHistory>();
	/**
	 * Comments regarding this visit
	 */
	private String comments;
	/**
	 * The Cost of this Vet Visit
	 */
	private float cost = 0;
	
	/**
	 * Get the Business details that did this visit
	 * @return Vet Business
	 */
	public Business getVet(){
		Owner o = LivestockSolutions.getOwnerByID(vet);
		if(o!=null&&!(o instanceof Business)){
			// Change back type to Business
			LivestockSolutions.addOwner(o=o.changeType(Business.class));
		}
		return (Business)o;
	}
	
	/**
	 * Set the ID of this MedicalHistory.
	 * @param id The new id of this MedicalHistory.
	 */
	public void setID(long id){
		if(this.id==0){
			this.id=id;
		}
	}
	
	/**
	 * Get the id of this MedicalHistory item.
	 * @return The id of this MedicalHistory.
	 */
	public long getID(){
		return id;
	}
	
	/**
	 * Get the date of this visit
	 * @return the date this visit occurred, <tt>null</tt> if date is not set.
	 */
	public Date getDate(){
		return date==0?null:new Date(date);
	}
	
	/**
	 * Get a list of medicines prescribed during this visit
	 * @return list of Medicines prescribed
	 */
	public MedicineHistory[] getPrescribedMedicines(){
		return prescribedMedicines.values().toArray(new MedicineHistory[]{});
	}
	
	/**
	 * Get the comments added to this visit
	 * @return Comments about this visit
	 */
	public String getComments(){
		return comments==null?"":comments;
	}
	
	/**
	 * Get the cost of just the vet visit.
	 * @return Cost of this visit.
	 */
	public float getCost(){
		return cost;
	}
	
	/**
	 * Get the cost of this visit including any costs for medicines.
	 * @return Total cost of this visit.
	 */
	public float getTotalCost(){
		float c = cost;
		for(MedicineHistory mh:prescribedMedicines.values()){
			c+=mh.getCost();
		}
		return c;
	}
	
	/**
	 * Set the Vet for this visit
	 * @param vet vet/business that were called out for this visit
	 */
	public void setVet(Business vet){
		if(this.vet!=vet.getID()){
			this.vet=vet.getID();
			update.put("VET", new UpdateHistory(String.valueOf(this.vet)));
		}
	}
	
	/**
	 * Set the date this visit occurred
	 * @param date date of the visit
	 */
	public void setDate(Date date){
		if(date!=null && this.date!=date.getTime()){
			this.date=date.getTime();
			update.put("DATE", new UpdateHistory(String.valueOf(this.date)));
		}
	}
	
	/**
	 * Set a comment regarding this visit.
	 * @param comment the comment to set for this visit.
	 */
	public void setComments(String comment){
		if(comment!=null && !comment.equals(this.comments)){
			this.comments = comment;
			update.put("COMMENT", new UpdateHistory(this.comments));
		}
	}
	
	/**
	 * Set the cost of this visit.
	 * @param cost of this visit excluding any cost for any medicines added.
	 */
	public void setCost(float cost){
		if(this.cost!=cost){
			this.cost = cost;
			update.put("COST", new UpdateHistory(String.valueOf(this.cost)));
		}
	}
	
	/**
	 * Add a prescribed medicine to this visit
	 * @param medicine medicine to add to this visit
	 */
	public void addMedicine(MedicineHistory medicine){
		if(medicine.getID()==0){
			// Adding a new Contact
			long newID = new Date().getTime();
			while(prescribedMedicines.containsKey(newID)){
				newID++;
			}
			medicine.setID(newID);
		}
		if(!prescribedMedicines.containsKey(medicine.getID())){
			prescribedMedicines.put(medicine.getID(),medicine);
			
			UpdateHistory medicineList = update.get("MEDICINE")!=null? update.get("MEDICINE"):new UpdateHistory();
			medicineList.setValues(String.valueOf(medicine.getID()),new UpdateHistory("true"));
			update.put("MEDICINE", medicineList);
		}
	}
	
	/**
	 * Remove a prescribed medicine from this visit
	 * @param medicine medicine to remove this visit
	 */
	public boolean removeMedicine(MedicineHistory medicine){
		if(prescribedMedicines.remove(medicine.getID())!=null){
			UpdateHistory medicineList = update.get("MEDICINE")!=null? update.get("MEDICINE"):new UpdateHistory();
			medicineList.setValues(String.valueOf(medicine.getID()),new UpdateHistory("false"));
			update.put("MEDICINE", medicineList);
			return true;
		}
		return false;
	}
	
	/**
	 * Updates this Object with the updated values from the different version.
	 * @param changes MedicalHistory that represents this Object in a different version.
	 * @return <tt>true</tt> if changes is not null and changes does not equal this object, <tt>false</tt> otherwise
	 */
	public boolean update(MedicalHistory changes){
		if(changes!=null && this!=changes){
			if(updateBefore("DATE",changes)){
				date=Long.parseLong(changes.update.get("DATE").getValue());
				update.put("DATE", new UpdateHistory(String.valueOf(date)));
			}
			
			if(updateBefore("VET",changes)){
				vet=Long.parseLong(changes.update.get("VET").getValue());
				update.put("VET", new UpdateHistory(String.valueOf(vet)));
			}
			
			if(updateBefore("COMMENT",changes)){
				comments=changes.update.get("COMMENT").getValue();
				update.put("COMMENT", new UpdateHistory(comments));
			}
			
			if(updateBefore("COST",changes)){
				cost=Float.parseFloat(changes.update.get("COST").getValue());
				update.put("COST", new UpdateHistory(String.valueOf(cost)));
			}
			
			if(changes.update.get("MEDICINE")!=null){
				for(String key : changes.update.get("MEDICINE").toKeyArray()){
					if(update.get("MEDICINE")==null || update.get("MEDICINE").getValue(key)==null || !update.get("MEDICINE").getValue(key).getDate().after(changes.update.get("MEDICINE").getValue(key).getDate())){
						UpdateHistory medicineList = update.get("MEDICINE")!=null?update.get("MEDICINE"):new UpdateHistory();
						MedicineHistory m=null;
						if((m =prescribedMedicines.get(Long.parseLong(key)))!=null){
							if(changes.update.get("MEDICINE").getValue(key).equals("false")){
								// Remove
								prescribedMedicines.remove(m);
								medicineList.setValues(key,new UpdateHistory("false"));
							}else{
								// Add
								m.update(changes.prescribedMedicines.get(key));
								prescribedMedicines.put(m.getID(),m);
								medicineList.setValues(key,new UpdateHistory("true"));
							}
						}else if(changes.update.get("MEDICINE").getValue(key).equals("true")){
							m=changes.prescribedMedicines.get(key);
							prescribedMedicines.put(m.getID(),m);
						}
						update.put("MEDICINE", medicineList);
					}
				}
			}
			return true;
		}
		return false;
	}
	
	/**
	 * Checks to see if the modification date of the item at this position is before the changed value
	 * @param key The String key at which to compare the modification dates of the modified objects.
	 * @param changes The MedicalHistory that represents this MedicalHistory on another system.
	 * @return <tt>true</tt> if the old value was modified before the changed value. <tt>false</tt> otherwise.
	 */
	protected boolean updateBefore(String key,MedicalHistory changes){
		if(this.update.get(key)==null && changes!=null && changes.update.get(key)!=null){
			return true;
		}else if(changes.update.get(key)==null){
			return false;
		}
		return !this.update.get(key).getDate().after(changes.update.get(key).getDate());
	}
}
